package com.xiaoger.aop.aop.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * 日志切面
 *
 * @author xjx
 * @date 2022/5/6 16:37
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    /**
     * 定义切面表达式
     * 1、execution(): 表达式主体。
     * 2、第一个*号：表示返回类型，*号表示所有的类型。
     * 3、包名：表示需要拦截的包名，后面的两个句点表示当前包和当前包的所有子包，com.sample.service.impl包、子孙包下所有类的方法。
     * 4、第二个*号：表示类名，*号表示所有的类。
     * 5、*(..):最后这个星号表示方法名，*号表示所有的方法，后面括弧里面表示方法的参数，两个句点表示任何参数。
     */
    @Pointcut("execution(public * com.xiaoger.aop.aop.controller.*.*(..))")
    public void webLog() {
    }

    /**
     * 在被切入的方法之前执行
     *
     * @param joinPoint
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("=============@Before方法开始执行=================");
        // 获取到请求相关信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //记录请求相关内容
        log.info("请求uri为：{}", request.getRequestURI());
        log.info("请求url为：{}", request.getRequestURL().toString());
        log.info("请求方式为：{}", request.getMethod());
        log.info("请求IP地址为：{}", request.getRemoteAddr());
        log.info("请求类的方法为：{}", joinPoint.getSignature());
        log.info("被代理的对象为：{}", joinPoint.getTarget());
        log.info("代理对象自己为：{}", joinPoint.getThis());
        log.info("请求类的相对路径为：{}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("请求类参数为：{}", Arrays.asList(joinPoint.getArgs()));
        log.info("=============@Before方法结束执行=================");
    }

    /**
     * 在切入的方法最后执行，类似于final,被切入的方法是否抛出异常都会执行该方法
     *
     * @param joinPoint
     */
    @After("webLog()")
    public void doAfter(JoinPoint joinPoint) {
        log.info("=============@After方法开始执行=================");
        log.info("执行了@After方法,被切入的方法是否抛出异常都会执行该方法");
        log.info("=============@After方法结束执行=================");
    }

    /**
     * 在被切入的方法执行异常之后执行该方法，执行顺序在@After之前
     *
     * @param joinPoint
     * @param ex        异常信息
     */
    @AfterThrowing(value = "webLog()", throwing = "ex")
    public void doAfterThrow(JoinPoint joinPoint, Throwable ex) {
        log.info("=============@AfterThrowing方法开始执行=================");
        log.info("当前方法执行出现了异常,异常信息为：{}", ex.getMessage());
        log.info("=============@AfterThrowing方法结束执行=================");
    }

    /**
     * 在被切入方法返回值后执行,如果被切入方法执行报错，将不会执行@AfterReturning方法，同样的执行顺序在@After之前
     *
     * @param joinPoint
     * @param result    返回结果
     */
    @AfterReturning(value = "webLog()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        log.info("=============@AfterReturning方法开始执行=================");
        log.info("被切入方法返回值为：{}", result);
        log.info("=============@AfterReturning方法结束执行=================");
    }

    /**
     * 环绕通知，在被切入方法前后执行，而且可以拿到被切入方法的返回值
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("=============@Around方法开始执行=================");
        Object proceed = new Object();
        try {
            proceed = joinPoint.proceed();
            log.info("方法环绕proceed,结果是：{}", proceed);
            log.info("=============@Around方法结束执行=================");
            return proceed;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return proceed;
    }

}
